package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"sync"
	"syscall"
	"time"

	"gopkg.in/yaml.v3"
)

// Config 配置文件结构
type Config struct {
	Tasks []Task `yaml:"tasks"`
}

// Task 单个任务配置
type Task struct {
	Name     string            `yaml:"name"`
	URL      string            `yaml:"url"`
	Method   string            `yaml:"method"`
	Interval string            `yaml:"interval"`
	Headers  map[string]string `yaml:"headers"`
	Body     string            `yaml:"body"`
	Enabled  bool              `yaml:"enabled"`
}

// TaskScheduler 任务调度器
type TaskScheduler struct {
	tasks  []Task
	stopCh chan struct{}
	wg     sync.WaitGroup
}

func main() {
	// 定义命令行参数
	configPath := flag.String("config", "config.yaml", "配置文件路径")
	flag.Parse()

	// 获取配置文件的绝对路径（相对于可执行文件所在目录）
	configFile := getConfigPath(*configPath)

	// 读取配置文件
	config, err := loadConfig(configFile)
	if err != nil {
		log.Fatalf("加载配置文件失败: %v", err)
	}

	// 创建任务调度器
	scheduler := NewTaskScheduler(config.Tasks)

	// 启动所有任务
	scheduler.Start()

	// 等待中断信号
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)

	fmt.Println("定时请求任务已启动，按 Ctrl+C 停止...")
	fmt.Printf("使用配置文件: %s\n", configFile)
	<-sigCh

	// 停止所有任务
	fmt.Println("\n正在停止所有任务...")
	scheduler.Stop()
	fmt.Println("所有任务已停止")
}

// getConfigPath 获取配置文件路径（相对于可执行文件目录）
func getConfigPath(configPath string) string {
	// 如果是绝对路径，直接返回
	if filepath.IsAbs(configPath) {
		return configPath
	}

	// 获取可执行文件所在目录
	exePath, err := os.Executable()
	if err != nil {
		log.Printf("警告: 无法获取可执行文件路径: %v，使用当前目录", err)
		return configPath
	}

	exeDir := filepath.Dir(exePath)
	return filepath.Join(exeDir, configPath)
}

// loadConfig 从文件加载配置
func loadConfig(filename string) (*Config, error) {
	data, err := os.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("读取配置文件失败: %w", err)
	}

	var config Config
	if err := yaml.Unmarshal(data, &config); err != nil {
		return nil, fmt.Errorf("解析配置文件失败: %w", err)
	}

	return &config, nil
}

// NewTaskScheduler 创建新的任务调度器
func NewTaskScheduler(tasks []Task) *TaskScheduler {
	return &TaskScheduler{
		tasks:  tasks,
		stopCh: make(chan struct{}),
	}
}

// Start 启动所有启用的任务
func (ts *TaskScheduler) Start() {
	for _, task := range ts.tasks {
		if !task.Enabled {
			log.Printf("[%s] 任务已禁用，跳过", task.Name)
			continue
		}

		// 解析时间间隔
		duration, err := time.ParseDuration(task.Interval)
		if err != nil {
			log.Printf("[%s] 时间间隔格式错误: %v", task.Name, err)
			continue
		}

		ts.wg.Add(1)
		go ts.runTask(task, duration)
	}
}

// Stop 停止所有任务
func (ts *TaskScheduler) Stop() {
	close(ts.stopCh)
	ts.wg.Wait()
}

// runTask 运行单个任务
func (ts *TaskScheduler) runTask(task Task, interval time.Duration) {
	defer ts.wg.Done()

	// 立即执行一次
	ts.executeRequest(task)

	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			ts.executeRequest(task)
		case <-ts.stopCh:
			log.Printf("[%s] 任务已停止", task.Name)
			return
		}
	}
}

// executeRequest 执行HTTP请求
func (ts *TaskScheduler) executeRequest(task Task) {
	startTime := time.Now()

	// 创建请求
	var req *http.Request
	var err error

	if task.Method == "POST" && task.Body != "" {
		req, err = http.NewRequest(task.Method, task.URL, bytes.NewBufferString(task.Body))
	} else {
		req, err = http.NewRequest(task.Method, task.URL, nil)
	}

	if err != nil {
		log.Printf("[%s] 创建请求失败: %v", task.Name, err)
		return
	}

	// 设置请求头
	for key, value := range task.Headers {
		req.Header.Set(key, value)
	}

	// 发送请求
	client := &http.Client{
		Timeout: 30 * time.Second,
	}

	resp, err := client.Do(req)
	if err != nil {
		log.Printf("[%s] 请求失败: %v", task.Name, err)
		return
	}
	defer resp.Body.Close()

	// 读取响应
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Printf("[%s] 读取响应失败: %v", task.Name, err)
		return
	}

	duration := time.Since(startTime)

	// 记录结果
	log.Printf("[%s] %s %s - 状态码: %d - 耗时: %v - 响应长度: %d bytes",
		task.Name,
		task.Method,
		task.URL,
		resp.StatusCode,
		duration,
		len(body),
	)

	// 如果需要查看响应内容，取消下面的注释
	// log.Printf("[%s] 响应内容: %s", task.Name, string(body))
}
